package net.nshc.kwkim.mycalc.temp;
import java.io.*;
import java.util.*;


public class Calculator {
	public static void main(String[] args)
	{
		try {
			InputStreamReader isr = new InputStreamReader(System.in);
			BufferedReader lineSource = new BufferedReader(isr);
			isr = null;
			String prompt = ">> ";

			System.out.println("\nInfix calculator program starting up ...");
			System.out.println("When you see this prompt: " + prompt);
			System.out.println("enter an infix expression, or q to quit.\n");

			while (true) {
				System.out.print(prompt);
				String inputLine = lineSource.readLine();
				if (inputLine.length() >= 1 && inputLine.charAt(0) == 'q')
					break;

				Vector<Token> tokenList = tokenize(inputLine);
				if (tokenList == null)
					System.out.println("Illegal characters in input.");
				else {
					System.out.println("Tokens in input are:");
					for (int i = 0; i < tokenList.size(); i++)
						System.out.print(" " + tokenList.elementAt(i));
					System.out.println();

					Vector<Token> postfixTokens = infixToPostfix(tokenList);
					System.out.println("Tokens in postfix translation are:");
					for (int i = 0; i < postfixTokens.size(); i++)
						System.out.print(" " + postfixTokens.elementAt(i));

					int expressionValue = evaluatePostfix(postfixTokens);
					System.out.println("Value of expression is: " + expressionValue);
				}   // else
			}     // while
		}       // try
		catch (Exception e) {
			// This is NOT a user-frindly way to deal with an error,
			// but it is acceptable for this lab exercise.
			e.printStackTrace();
		}
	}

	public static Vector<Token> tokenize(String s)
	{
		Vector<Token> result = new Vector<Token>();
		int index = 0;            // index into s
		while (index < s.length())
			if (Character.isWhitespace(s.charAt(index)))
				index++;
			else if (Character.isDigit(s.charAt(index))) {
				StringBuffer digitSequence = new StringBuffer();
				while (index < s.length() && Character.isDigit(s.charAt(index))) {
					digitSequence.append(s.charAt(index));
					index++;
				}
				Token newToken = new Token(true, Integer.parseInt(digitSequence.toString()));
				result.addElement(newToken);
			}
			else {
				switch (s.charAt(index)) {
				case '+': 
					result.addElement(new Token(false, Token.ADD)); break;
				case '-': 
					result.addElement(new Token(false, Token.SUBTRACT)); break;
				case '*':
					result.addElement(new Token(false, Token.MULTIPLY)); break;
				case '/':
					result.addElement(new Token(false, Token.DIVIDE)); break;
				case '^': 
					result.addElement(new Token(false, Token.POWER)); break;
				case '(':
					result.addElement(new Token(false, Token.OPEN_PAR)); break;
				case ')':
					result.addElement(new Token(false, Token.CLOSE_PAR)); break;
				default:
					// Found a char that does not make sense.
					return null;
				}
				index++;
			}
		result.addElement(new Token(false, Token.END));
		return result;
	}

	public static Vector<Token> infixToPostfix(Vector<Token> infix)
	{
		StackOfTokens tokenStack = new StackOfTokens();
		tokenStack.push(new Token(false, Token.BEGIN));

		Vector<Token> result = new Vector<Token>();
		for (int i = 0; i < infix.size(); i++) {
			Token current = (Token) infix.elementAt(i);
			if (current.isNumber())
				result.addElement(current);
			else if (current.kindOfOperator() == Token.OPEN_PAR)
				tokenStack.push(current);
			else if (current.kindOfOperator() == Token.CLOSE_PAR) {
				while (tokenStack.top().kindOfOperator() != Token.OPEN_PAR) {
					result.addElement(tokenStack.top());
					tokenStack.pop();
				}
				tokenStack.pop();
			}
			else {
				while (Token.preferLeft(tokenStack.top(), current)) {
					result.addElement(tokenStack.top());
					tokenStack.pop();
				}
				tokenStack.push(current);
			}
		}
		return result;
	}

	public static int evaluatePostfix(Vector<Token> postfix)
	{
		StackOfInts intStack = new StackOfInts();
		for (int i = 0; i < postfix.size(); i++) {
			Token current = (Token) postfix.elementAt(i);
			if (current.isNumber())
				intStack.push(current.value());
			else {
				int rightOperand = intStack.top();
				intStack.pop();
				int leftOperand = intStack.top();
				intStack.pop();
				int operationResult = 0;
				switch(current.kindOfOperator()) {
				case Token.ADD: 
					operationResult = leftOperand + rightOperand;
					break;
				case Token.SUBTRACT: 
					operationResult = leftOperand - rightOperand;
					break;
				case Token.MULTIPLY: 
					operationResult = leftOperand * rightOperand;
					break;
				case Token.DIVIDE: 
					operationResult = leftOperand / rightOperand;
					break;
				case Token.POWER:
					operationResult = 1;
					for (int count = rightOperand; count > 0; count--)
						operationResult *= leftOperand;
					break;
				default:
					throw new RuntimeException("default reached in evaluatePostfix.");
				} // switch
				intStack.push(operationResult);
			} // else
		} // for
		return intStack.top();
	}
}
